/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry.protocol;

import io.sentry.IUnknownPropertiesConsumer;
import io.sentry.util.CollectionUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

/**
 * Information about the user who triggered an event.
 *
 * <p>```json { "user": { "id": "unique_id", "username": "my_user", "email": "foo@example.com",
 * "ip_address": "127.0.0.1", "subscription": "basic" } } ```
 *
 * @since 2021-06-02
 */
public final class User implements Cloneable, IUnknownPropertiesConsumer {
    /**
     * Email address of the user.
     */
    private @Nullable String email;

    /**
     * Unique identifier of the user.
     */
    private @Nullable String id;

    /**
     * Username of the user.
     */
    private @Nullable String username;

    /**
     * Remote IP address of the user.
     */
    private @Nullable String ipAddress;

    /**
     * Additional arbitrary fields, as stored in the database (and sometimes as sent by clients). All
     * data from `self.other` should end up here after store normalization.
     */
    private @Nullable Map<String, @NotNull String> other;

    /**
     * unknown fields, only internal usage.
     */
    private @Nullable Map<String, @NotNull Object> unknown;

    /**
     * Gets the e-mail address of the user.
     *
     * @return the e-mail.
     */
    public @Nullable String getEmail() {
        return email;
    }

    /**
     * Gets the e-mail address of the user.
     *
     * @param email the e-mail.
     */
    public void setEmail(final @Nullable String email) {
        this.email = email;
    }

    /**
     * Gets the id of the user.
     *
     * @return the id.
     */
    public @Nullable String getId() {
        return id;
    }

    /**
     * Sets the id of the user.
     *
     * @param id the user id.
     */
    public void setId(final @Nullable String id) {
        this.id = id;
    }

    /**
     * Gets the username of the user.
     *
     * @return the username.
     */
    public @Nullable String getUsername() {
        return username;
    }

    /**
     * Sets the username of the user.
     *
     * @param username the username.
     */
    public void setUsername(final @Nullable String username) {
        this.username = username;
    }

    /**
     * Gets the IP address of the user.
     *
     * @return the IP address of the user.
     */
    public @Nullable String getIpAddress() {
        return ipAddress;
    }

    /**
     * Sets the IP address of the user.
     *
     * @param ipAddress the IP address of the user.
     */
    public void setIpAddress(final @Nullable String ipAddress) {
        this.ipAddress = ipAddress;
    }

    /**
     * Gets other user related data.
     *
     * @return the other user data.
     */
    public @Nullable Map<String, @NotNull String> getOthers() {
        return other;
    }

    /**
     * Sets other user related data.
     *
     * @param others the other user related data..
     */
    public void setOthers(final @Nullable Map<String, @NotNull String> others) {
        this.other = CollectionUtils.newConcurrentHashMap(others);
    }

    /**
     * User's unknown fields, only internal usage
     *
     * @param unknowns the unknown fields
     */
    @ApiStatus.Internal
    @Override
    public void acceptUnknownProperties(final @NotNull Map<String, Object> unknowns) {
        this.unknown = new ConcurrentHashMap<>(unknowns);
    }

    /**
     * the User's unknown fields
     *
     * @return the unknown map
     */
    @TestOnly
    @Nullable
    Map<String, @NotNull Object> getUnknown() {
        return unknown;
    }

    /**
     * Clones an User aka deep copy
     *
     * @return the cloned User
     * @throws CloneNotSupportedException if the User is not cloneable
     */
    @Override
    public @NotNull User clone() throws CloneNotSupportedException {
        final User clone = (User) super.clone();

        clone.other = CollectionUtils.newConcurrentHashMap(other);
        clone.unknown = CollectionUtils.newConcurrentHashMap(unknown);

        return clone;
    }
}
